package com.qd.panda.service.park;

import com.cdqidi.constant.ConstantDto;
import com.cdqidi.dto.ApiResult;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.RedisTemplateUtil;
import com.qd.common.panda.domain.entity.carpark.CarParkInfoDTO;
import com.qd.panda.model.CarParkAppointmentSet;
import com.qd.panda.service.stroke.BigDecimalUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author sjk
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class ParkCacheService {
    private static final String TIME_OUT_MSG = "获得redis锁超时";
    private final RedissonClient redissonClient;
    private final RedisTemplateUtil<CarParkAppointmentSet> redisTemplateUtil;
    private final CarParkAppointmentSetService carParkAppointmentSetService;
    protected static final Map<String, Boolean> NUM_CACHE = new ConcurrentHashMap<>();

    public CarParkAppointmentSetService getCarParkAppointmentSetService() {
        return carParkAppointmentSetService;
    }

    /**
     * 预约数量+1
     *
     * @param parkId 停车场ID
     * @return 当前停车场预约数量
     */
    public int incr(String parkId) {
        CarParkAppointmentSet appointmentSet = carParkAppointmentSetService.getById(parkId);
        if (null == appointmentSet) {
            //说明不是可预约的停车场
            return -1;
        }
        Integer num = appointmentSet.getNum();
        RLock lock = redissonClient.getLock(parkId + "appointment");
        try {
            try {
                boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                if (res) {
                    if (Boolean.FALSE.equals(NUM_CACHE.getOrDefault(parkId, false))) {
                        return loadAppointmentNum(parkId);
                    }
                    RedisAtomicInteger entityIdCounter = new RedisAtomicInteger(RedisTemplateUtil.getRedisCacheKey(CarParkAppointmentSetService.PREFIX_NUM, parkId), Objects.requireNonNull(redisTemplateUtil.getStringRedisTemplate().getConnectionFactory()));
                    int i = entityIdCounter.get() + 1;
                    if (i <= num) {
                        entityIdCounter.set(i);
                    }
                    final int i1 = entityIdCounter.get();
                    log.info("当前停车场,预约数量+1: {}", i1);
                    return i1;
                } else {
                    throw new ApiException(ApiResult.error());
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error(TIME_OUT_MSG, e);
                throw new ApiException(ApiResult.error());
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 预约数量-1
     *
     * @param parkId 停车场ID
     * @return 当前停车场预约数量
     */
    public int decr(String parkId) {
        CarParkAppointmentSet appointmentSet = carParkAppointmentSetService.getById(parkId);
        if (null == appointmentSet) {
            //说明不是可预约的停车场
            return -1;
        }
        RLock lock = redissonClient.getLock(parkId + "appointment");
        try {
            try {
                boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                if (res) {
                    if (Boolean.FALSE.equals(NUM_CACHE.getOrDefault(parkId, false))) {
                        return loadAppointmentNum(parkId);
                    }
                    RedisAtomicInteger entityIdCounter = new RedisAtomicInteger(RedisTemplateUtil.getRedisCacheKey(CarParkAppointmentSetService.PREFIX_NUM, parkId), Objects.requireNonNull(redisTemplateUtil.getStringRedisTemplate().getConnectionFactory()));
                    int i = entityIdCounter.get() - 1;
                    if (i > -1) {
                        entityIdCounter.set(i);
                    }
                    int i1 = entityIdCounter.get();
                    log.info("当前停车场,预约数量-1: {}", i1);
                    return i1;
                } else {
                    throw new ApiException(ApiResult.error());
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error(TIME_OUT_MSG, e);
                throw new ApiException(ApiResult.error());
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 前往停车场停车数量-1
     *
     * @param parkId 停车场ID
     * @return 前往停车场的车辆数
     */
    public int goToDecr(String parkId) {
        CarParkAppointmentSet appointmentSet = carParkAppointmentSetService.getById(parkId);
        if (null == appointmentSet) {
            //说明不是可预约的停车场
            return -1;
        }
        RLock lock = redissonClient.getLock(parkId + "goTo");
        try {
            try {
                boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                if (res) {
                    RedisAtomicInteger entityIdCounter = new RedisAtomicInteger(RedisTemplateUtil.getRedisCacheKey(CarParkAppointmentSetService.PREFIX_GO_TO_NUM, parkId), Objects.requireNonNull(redisTemplateUtil.getStringRedisTemplate().getConnectionFactory()));
                    int i = entityIdCounter.get() - 1;
                    if (i > -1) {
                        entityIdCounter.set(i);
                    }
                    return entityIdCounter.get();
                } else {
                    throw new ApiException(ApiResult.error());
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error(TIME_OUT_MSG, e);
                throw new ApiException(ApiResult.error());
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 前往停车场停车数量+1
     *
     * @param parkId 停车场ID
     * @return 当前停车场预约数量
     */
    public int goToIncr(String parkId) {
        CarParkAppointmentSet appointmentSet = carParkAppointmentSetService.getById(parkId);
        if (null == appointmentSet) {
            //说明不是可预约的停车场
            return -1;
        }
        Integer num = appointmentSet.getNum();
        RLock lock = redissonClient.getLock(parkId + "goTo");
        try {
            try {
                boolean res = lock.tryLock(30, 10, TimeUnit.SECONDS);
                if (res) {
                    RedisAtomicInteger entityIdCounter = new RedisAtomicInteger(RedisTemplateUtil.getRedisCacheKey(CarParkAppointmentSetService.PREFIX_GO_TO_NUM, parkId), Objects.requireNonNull(redisTemplateUtil.getStringRedisTemplate().getConnectionFactory()));
                    int i = entityIdCounter.get() + 1;
                    if (i <= num) {
                        entityIdCounter.set(i);
                    }
                    return entityIdCounter.get();
                } else {
                    throw new ApiException(ApiResult.error());
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error(TIME_OUT_MSG, e);
                throw new ApiException(ApiResult.error());
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 加载剩余预约车位数量到redis中
     *
     * @param parkId 停车场ID
     * @return 剩余的预约车位数量
     */
    public int loadAppointmentNum(String parkId) {
        String key = RedisTemplateUtil.getRedisCacheKey(CarParkAppointmentSetService.PREFIX_NUM, parkId);
        //不存在的话加载到缓存中去
        RedisAtomicInteger entityIdCounter = new RedisAtomicInteger(key, Objects.requireNonNull(redisTemplateUtil.getStringRedisTemplate().getConnectionFactory()));
        int appointmentNum = appointmentNum(parkId);
        entityIdCounter.set(appointmentNum);
        log.info("loadAppointmentNum,parkId:{},appointmentNum:{}", parkId, appointmentNum);
        //存入本地缓存
        NUM_CACHE.put(parkId, Boolean.TRUE);
        return appointmentNum;
    }

    /**
     * 获得剩余预约的数量
     *
     * @param parkId 停车场ID
     * @return 预约数量
     */
    public Integer appointmentNum(String parkId) {
        //已经预约的数量
        Integer appointmentNum = carParkAppointmentSetService.getBaseMapper().appointmentNum(parkId);
        CarParkAppointmentSet carParkAppointmentSet = carParkAppointmentSetService.getById(parkId);
        if (null == carParkAppointmentSet) {
            throw new ApiException("停车场预约设置不存在");
        }
        Integer num = carParkAppointmentSet.getNum();
        if (null == num || num < 1) {
            throw new ApiException("停车场预约数量设置异常");
        }
        return num - appointmentNum;
//        return appointmentNum;
    }


    /**
     * 获得预约数量
     *
     * @param parkId 停车场ID
     * @return 已经预约的数量
     */
    public int getAppointmentNum(String parkId) {
        final String key = RedisTemplateUtil.getRedisCacheKey(CarParkAppointmentSetService.PREFIX_NUM, parkId);
        if (Boolean.TRUE.equals(redisTemplateUtil.getStringRedisTemplate().hasKey(key))) {
            RedisAtomicInteger entityIdCounter = new RedisAtomicInteger(key, Objects.requireNonNull(redisTemplateUtil.getStringRedisTemplate().getConnectionFactory()));
            final int i = entityIdCounter.get();
            if (log.isDebugEnabled()) {
                log.debug("获得剩余预约数量，直接从缓存中去取,parkId: {},数量: {}", parkId, i);
            }
            return i;
        }
        return loadAppointmentNum(parkId);
    }


    public void buildCarParkApp(CarParkInfoDTO carParkInfoDTO) {
        CarParkAppointmentSet carParkAppointmentSet = carParkAppointmentSetService.getById(carParkInfoDTO.getPId());
        carParkInfoDTO.setIsAppointmentSet((null == carParkAppointmentSet) ? ConstantDto.SF0 : ConstantDto.SF1);
        if (null == carParkAppointmentSet) {
            carParkInfoDTO.setIsAppointmentSet(ConstantDto.SF0);
        } else {
            Integer num = carParkAppointmentSet.getNum();
            carParkInfoDTO.setIsAppointmentSet(ConstantDto.SF1);
            //前往停车场停车的车辆数目
            carParkInfoDTO.setGoToParkNum(getGotoNum(carParkInfoDTO.getPId()));
            //已经预约的车位数量
            int appointmentNum = getAppointmentNum(carParkInfoDTO.getPId());
            if (appointmentNum >= num) {
                carParkInfoDTO.setBerthStatus("泊位已满");
            } else if (BigDecimalUtil.percentage(appointmentNum, num) > 70) {
                carParkInfoDTO.setBerthStatus("泊位紧张");
            } else {
                carParkInfoDTO.setBerthStatus("泊位充足");
            }
        }
    }

    /**
     * 获得前往停车场停车的数量
     *
     * @param parkId 停车场ID
     * @return 已经预约的数量
     */
    public int getGotoNum(String parkId) {
        RedisAtomicInteger entityIdCounter = new RedisAtomicInteger(RedisTemplateUtil.getRedisCacheKey(CarParkAppointmentSetService.PREFIX_GO_TO_NUM, parkId), Objects.requireNonNull(redisTemplateUtil.getStringRedisTemplate().getConnectionFactory()));
        return entityIdCounter.get();
    }

}
